package org.sgx.jsutil.client;

import java.util.Collection;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayMixed;
import com.google.gwt.core.client.JsArrayString;

/**
 * a better JavaScriptObject - with added methods for setting, getting properties, creating objects from java code, etc. see also JsArr . all methods prefixed with obj for not
 * interfeiing with client subclasses.
 * 
 * for creation, getters and setters it support a short method "_"
 * 
 * @author sg
 * 
 */
public class JsObject extends JavaScriptObject {
	protected JsObject() {
	}

	public static native final JsObject create(Object propName, Object val)/*-{
		var o = {};
		o[propName] = val;
		return o;
	}-*/;

	/**
	 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/ToSource
	 * @return a string representing the source code for the function.
	 * 
	 *         <p>
	 *         The <code>toSource</code> method returns the following values:
	 *         </p>
	 * 
	 *         <ul>
	 *         <li>For the built-in <code>Function</code> object, <code>toSource</code> returns the following string indicating that the source code is not available:</li>
	 *         </ul>
	 * 
	 *         <pre>
	 * function Function() {
	 *     [native code]
	 * }
	 * </pre>
	 *         <ul>
	 *         <li>For custom functions, <code>toSource</code> returns the JavaScript source that defines the object as a string.</li>
	 *         </ul>
	 *         <p>
	 *         This method is usually called internally by JavaScript and not explicitly in code. You can call <code>toSource</code> while debugging to examine the contents of an
	 *         object.
	 *         </p>
	 */
	public final native String toSource_()/*-{
		return this.toSource();
	}-*/;

	/**
	 * @see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Function/Constructor
	 */
	public final native JsFunction constructor()/*-{
		return this.constructor();
	}-*/;

	// public static native final JsObject create(Object... vals)/*-{
	// // var arr = @org.sgx.yuigwt.yui.util.JsUtil::toj
	// var o = {};
	// o[propName] = val;
	// return o;
	// }-*/;

	// /**
	// * creates a new empty javascript object.
	// * @return a new empty javascript object.
	// */
	// public static final native JsObject empty()/*-{
	// return {};
	// }-*/;
	/**
	 * creates a new javascript object with the given property setted
	 * 
	 * @param pname
	 *            property name
	 * @param val
	 *            property value
	 * @return a new javascript object with the given property setted
	 */
	public static final native JsObject one(Object pname, Object val)/*-{
		var o = {};
		o[pname] = val;
		return o;
	}-*/;

	/**
	 * creates a new javascript object with the given property setted
	 * 
	 * @param pname
	 *            property name
	 * @param val
	 *            property value
	 * @return a new javascript object with the given property setted
	 */
	public static final native JsObject one(Object pname, String val)/*-{
		var o = {};
		o[pname] = val;
		return o;
	}-*/;

	/**
	 * creates a new javascript object with the given property setted
	 * 
	 * @param pname
	 *            property name
	 * @param val
	 *            property value
	 * @return a new javascript object with the given property setted
	 */
	public static final native JsObject one(Object pname, boolean val)/*-{
		var o = {};
		o[pname] = val;
		return o;
	}-*/;

	/**
	 * creates a new javascript object with the given property setted
	 * 
	 * @param pname
	 *            property name
	 * @param val
	 *            property value
	 * @return a new javascript object with the given property setted
	 */
	public static final native JsObject one(Object pname, int val)/*-{
		var o = {};
		o[pname] = val;
		return o;
	}-*/;

	/**
	 * creates a new javascript object with the given property setted
	 * 
	 * @param pname
	 *            property name
	 * @param val
	 *            property value
	 * @return a new javascript object with the given property setted
	 */
	public static final native JsObject one(Object pname, double val)/*-{
		var o = {};
		o[pname] = val;
		return o;
	}-*/;

	// public native final JsObject put(Object propertyName, String )

	public native final JsArrayMixed objGetArr(Object p)/*-{
		return this[p] || null;
	}-*/;

	public native final JsFunction objGetFunc(Object p)/*-{
		return this[p] || null;
	}-*/;

	public native final boolean objGetBoolean(Object p)/*-{
		return this[p] || false;
	}-*/;

	public native final JsObject objGetObj(Object p)/*-{
		return this[p] || null;
	}-*/;

	public native final String objGetString(Object p)/*-{
		return this[p] || null;
	}-*/;

	public native final int objGetInt(Object p)/*-{
		return this[p] || 0;
	}-*/;

	public native final int objGetDouble(Object p)/*-{
		return this[p] || 0;
	}-*/;

	public native final String objTypeof(Object p)/*-{
		return typeof (this[p]) + "";
	}-*/;

	public final native Object _(Object p)/*-{
		return this[p];
	}-*/;

	// public native final JsObject objPut(String pname, Object val)/*-{
	// this[pname]=val;
	// return this;
	// }-*/;
	public native final JsObject objPut(String pname, String val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject objPut(String pname, boolean val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject objPut(String pname, int val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject objPut(String pname, double val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject objPut(String pname, JsFunction val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	/**
	 * put/set a property to a Javascript object or function value.
	 * 
	 * @param pname
	 * @param val
	 * @return
	 */
	public native final JsObject objPut(String pname, JavaScriptObject val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	// public final native JsObject _(Object pname, Object val)/*-{
	// this[pname]=val;
	// return this;
	// }-*/;
	public native final JsObject _(String pname, String val)/*-{
		this[pname] = val + "";
		return this;
	}-*/;

	public native final JsObject _(String pname, Object val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject _(String pname, int val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject _(String pname, double val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	public native final JsObject _(String pname, JavaScriptObject val)/*-{
		this[pname] = val;
		return this;
	}-*/;

	/**
	 * @return this js object property names as a jnative js array
	 */
	public native final JsArrayString objProps()/*-{
		var props = [];
		for ( var i in this) {
			props.push(i + "");
		}
		return props;
	}-*/;

	/**
	 * @return this js object property names as a java collection
	 */
	public native final Collection<String> objPropsCol() /*-{
		return @org.sgx.jsutil.client.JsUtil::propsCol(Lorg/sgx/jsutil/client/JsObject;)(this);
		//		var props = this.@org.sgx.jsutil.client.JsObject::objProps()();
		//		return @org.sgx.jsutil.client.JsUtil::toJavaCollection(Lcom/google/gwt/core/client/JavaScriptObject;)(props);
	}-*/;

	public native final String objDump(boolean printValues)/*-{
		var s = "{";
		for ( var i in this) {
			s += i + (printValues ? ": " + obj[i] : "") + ", ";
		}
		return s + "}";
	}-*/;

	// method invoking - shortcuts for JsFunciton.apply

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a
	 * JavaScriptObject.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JavaScriptObject methodApplyObject(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a
	 * javascript array.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final JsArray methodApplyArray(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final void methodApply(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a string.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final String methodApplyString(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a number.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final double methodApplyDouble(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a
	 * Integer.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final double methodApplyInteger(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	/**
	 * Apply a funbctino property named 'methodName' this object instance as the this/context object and and 'args' arguments provided. In this case this function returns a
	 * Boolean.
	 * 
	 * @param thisArg
	 * @param args
	 * @return
	 */
	public native final boolean methodApplyBoolean(String methodName, JavaScriptObject args) /*-{
		return this[methodName].apply(this, args);
	}-*/;

	
	
	
}
